package lang

import (
	"jvm/native"
	"jvm/rtda"
	"jvm/rtda/heap"
	. "jvm/typedef"
)

const jlSystem = "java/lang/System"

func init() {
	native.Register(jlSystem, "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", arraycopy)
	native.Register(jlSystem, "setOut0", "(Ljava/io/PrintStream;)V", setOut0)
	// native.Register(jlSystem, "initProperties", "(Ljava/util/Properties;)Ljava/util/Properties;", initProperties)
}

// public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
func arraycopy(frame *rtda.Frame) {
	localVars := frame.LocalVars()
	src := localVars.GetRef(0)
	srcPos := localVars.GetInt(1)
	dest := localVars.GetRef(2)
	destPos := localVars.GetInt(3)
	length := localVars.GetInt(4)
	if src == nil || dest == nil {
		panic("java.lang.NullPointerException")
	}
	if !checkArrayCopy(src, dest) {
		panic("java.lang.ArrayStoreException")
	}
	if srcPos < 0 || destPos < 0 || length < 0 ||
		srcPos+length > I4(src.ArrayLength()) ||
		destPos+length > I4(dest.ArrayLength()) {
		panic("java.lang.IndexOutOfBoundsException")
	}
	heap.ArrayCopy(src, srcPos, dest, destPos, length)
}

// 判断两对象是否是数组且是否可以相互复制
func checkArrayCopy(src, dest *heap.Object) bool {
	srcClass := src.Class()
	destClass := dest.Class()
	if !srcClass.IsArray() || !destClass.IsArray() {
		return false
	}
	srcElement := srcClass.Name()[1:]
	destElement := destClass.Name()[1:]
	if (srcElement[0] == 'L' || srcElement[0] == '[') &&
		(destElement[0] == 'L' || destElement[0] == '[') {
		return true
	}
	return srcElement == destElement
}

// private static native void setOut0(PrintStream out);
func setOut0(frame *rtda.Frame) {
	out := frame.LocalVars().GetRef(0)
	sysClass := frame.Method().Class()
	sysClass.SetRefVar("out", "Ljava/io/PrintStream;", out)
}
